package mtsample.hotel.service;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import mtsample.hotel.dao.HotelAdminDao;
import mtsample.hotel.dao.HotelGuestDao;
import mtsample.hotel.dao.RentHistoryDao;
import mtsample.hotel.dao.RoomDao;
import mtsample.hotel.dao.TenantDao;
import mtsample.hotel.exception.TestException;
import mtsample.hotel.model.HotelAdmin;
import mtsample.hotel.model.HotelGuest;
import mtsample.hotel.model.RentHistory;
import mtsample.hotel.model.Room;
import mtsample.hotel.model.Tenant;
import mtsample.hotel.service.interfaces.IHotelService;


public class HotelService implements IHotelService{
	private static HashMap<String, String> admin_id_tenant_map = null;
	private static HashMap<String, String> admin_id_passwd_map = null;
	private static final String PU_BASE = "MT_HOTEL_SERVICE";
	private static final String TENANT_ID = "tenant.id";
	//should be different for each tenant
	private static double hotel_rate = 520.0;
	static {
		try{
			TenantDao tdao = new TenantDao(PU_BASE);
			tdao.start();
			List<Tenant> tenants = tdao.loadAll();
			tdao.end();
			printList(tenants);

			HotelAdminDao hadao = new HotelAdminDao(PU_BASE);
			hadao.start();
			List<HotelAdmin> admins = hadao.loadAll();
			hadao.end();
			printList(admins);
			admin_id_tenant_map = new HashMap<String, String>();
			admin_id_passwd_map = new HashMap<String, String>();
			for(HotelAdmin admin:admins){
				admin_id_tenant_map.put(admin.getLoginId(), admin.getTenantId());
				admin_id_passwd_map.put(admin.getLoginId(), admin.getPassword());
			}
		} catch (TestException te){
			te.printStackTrace();
		}
	}
	
	private Map<String, String> contextProps= null;
	private RoomDao roomDao = null;
	private HotelGuestDao guestDao = null;
	private RentHistoryDao historyDao = null;
	public boolean login_admin(String login_id, String passwd){
		//verify login
		if(passwd == null){
			return false;
		}
		if(passwd.equals(admin_id_passwd_map.get(login_id))){
			// if verified, set contextProperties with tenant.id 
			contextProps = new HashMap<String, String>();
			contextProps.put(TENANT_ID, admin_id_tenant_map.get(login_id));
			roomDao = new RoomDao(PU_BASE, contextProps);
			guestDao = new HotelGuestDao(PU_BASE, contextProps);
			historyDao = new RentHistoryDao(PU_BASE, contextProps);
			return true;
		}
		return false;
	}
	
	public List<Room> findRooms(String status){
		List<Room> results = null;
		try {
			roomDao.start();
			results = roomDao.queryRoomByStatus(status);
			roomDao.end();
		} catch (TestException te) {
			te.printStackTrace();
		}
		return results;
	}
	
	public List<Room> findRooms(String status, int bedNum){
		List<Room> results = null;
		try {
			roomDao.start();
			results = roomDao.queryRoomByStatusAndBedNum(status, bedNum);
			roomDao.end();
		} catch (TestException te) {
			te.printStackTrace();
		}
		return results;
	}
	
	public List<Room> findRooms(String status, String name){
		List<Room> results = null;
		try {
			roomDao.start();
			results = roomDao.queryRoomByStatusAndName(status, name);
			roomDao.end();
		} catch (TestException te) {
			te.printStackTrace();
		}
		return results;
	}
	
	public boolean checkin(String roomSerialNum, int hotelGuestId, Date in, Date out){
		try{
			historyDao.start();
			roomDao.start();// no need, but is there any possibility that RentHistory and Room use different different entitymanager
			
			// Transaction !!!
			historyDao.transactionBegin();
			roomDao.transactionBegin();
			try{
				// update Room status
				int roomId = roomDao.checkinRoom(roomSerialNum);
				// add RentHistory
				RentHistory history = new RentHistory();
				history.setRoomId(roomId);
				history.setHotelGuestId(hotelGuestId);
				history.setCreateTime(new Timestamp(Calendar.getInstance().getTimeInMillis()));
				history.setStartTime(in);
				history.setEndTime(out);
				history.setAmount(-1);
				//history.setTenantId(historyDao.getTenantId()); //No need with multi-tenancy support
				historyDao.add(history);
			} catch(Exception e){
				e.printStackTrace();
				historyDao.transactionRollBack();
				roomDao.transactionRollBack();
				return false;
			}
			roomDao.transactionCommit();
			historyDao.transactionCommit();
			// Transaction done!!!

			roomDao.end();
			historyDao.end();
		} catch (TestException te){
			te.printStackTrace();
			return false;
		}
		return true;
	}

	public boolean checkout(String roomSerialNum, Date out){
		try{
			historyDao.start();
			roomDao.start();// no need, but is there any possibility that RentHistory and Room use different different entitymanager
			
			// Transaction !!!
			historyDao.transactionBegin();
			roomDao.transactionBegin();
			try{
				// update Room status
				int roomId = roomDao.checkoutRoom(roomSerialNum);
				// update RentHistory
				List<RentHistory> histories = historyDao.queryRentHistoryByRoomIdAmountNotCheckout(roomId);
				RentHistory history = histories.get(0);
				history.setEndTime(out);
				history.setAmount(computeAmount(history.getStartTime(), out));
				//history.setTenantId(historyDao.getTenantId());
				historyDao.update(history);
			} catch(Exception e){
				e.printStackTrace();
				historyDao.transactionRollBack();
				return false;
			}
			roomDao.transactionCommit();
			historyDao.transactionCommit();
			// Transaction !!!

			roomDao.end();
			historyDao.end();
		} catch (TestException te){
			te.printStackTrace();
			return false;
		}
		return true;
	}
	
	public void addHotelGuest(HotelGuest guest){
		try{
			guestDao.start();
			guestDao.transactionBegin();
			guestDao.addHotelGuest(guest);
			guestDao.transactionCommit();
			guestDao.end();
		}catch (TestException te){
			te.printStackTrace();
		}
	}
	
	public List<HotelGuest> findAllHotelGuest(){
		List<HotelGuest> results = null;
		try{
			guestDao.start();
			results = guestDao.loadAll();
			guestDao.end();
		}catch(TestException te){
			te.printStackTrace();
		}
		return results;
	}

	public List<HotelGuest> findHotelGuestByName(String name){
		List<HotelGuest> results = null;
		try{
			guestDao.start();
			results = guestDao.queryHoteGuestByName(name);
			guestDao.end();
		}catch(TestException te){
			te.printStackTrace();
		}
		return results;
	}
	
	private double computeAmount(Date in, Date out){
		float interval_days = (float)( (out.getTime() - in.getTime()) / (float) (1000 * 60 * 60 * 24) );
		return hotel_rate * ( (interval_days - (int)interval_days >= 0.5) ? ((int)interval_days + 0.5): ((int)interval_days + 1));
	}
	
	private static <T> void print(T t){
		System.out.println(t);
	}
	
	private static <T> void printList(List<T> list){
		for (T t:list){
			print(t);
		}
	}

	@Override
	public List<RentHistory> findAllRentHistory() {
		List<RentHistory> results = null;
		try{
			historyDao.start();
			results = historyDao.loadAll();
			historyDao.end();
		}catch(TestException te){
			te.printStackTrace();
		}
		return results;
	}

	@Override
	public List<RentHistory> findRentHistoryByName(String name) {
		List<RentHistory> results = null;
		try{
			historyDao.start();
			results = historyDao.queryRentHistoryByHotelGuestName(name);
			historyDao.end();
		}catch(TestException te){
			te.printStackTrace();
		}
		return results;
	}
}
